{
 "cells": [
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "\n",
    "https://python.langchain.com/docs/how_to/tool_calling/\n",
    "\n",
    "https://python.langchain.com/docs/how_to/custom_tools/"
   ],
   "id": "bda5f943d3393e28"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "while the name \"tool calling\" implies that the model is directly performing some action, this is actually not the case! The model only generates the arguments to a tool, and actually running the tool (or not) is up to the user.",
   "id": "b4165cf3050bbb51"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "For a model to be able to call tools, we need to pass in tool schemas that describe what the tool does and what it's arguments are. Chat models that support tool calling features implement a .bind_tools() method for passing tool schemas to the model. Tool schemas can be passed in as Python functions (with typehints and docstrings), Pydantic models, TypedDict classes, or LangChain Tool objects. ",
   "id": "7c1941847bac853d"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-01T06:24:21.202267Z",
     "start_time": "2025-08-01T06:24:21.195732Z"
    }
   },
   "cell_type": "code",
   "source": [
    "# The function name, type hints, and docstring are all part of the tool\n",
    "# schema that's passed to the model. Defining good, descriptive schemas\n",
    "# is an extension of prompt engineering and is an important part of\n",
    "# getting models to perform well.\n",
    "def add(a: int, b: int) -> int:\n",
    "    \"\"\"Add two integers.\n",
    "\n",
    "    Args:\n",
    "        a: First integer\n",
    "        b: Second integer\n",
    "    \"\"\"\n",
    "    return a + b\n",
    "\n",
    "\n",
    "def multiply(a: int, b: int) -> int:\n",
    "    \"\"\"Multiply two integers.\n",
    "\n",
    "    Args:\n",
    "        a: First integer\n",
    "        b: Second integer\n",
    "    \"\"\"\n",
    "    return a * b\n",
    "\n",
    "tools = [add, multiply]"
   ],
   "id": "7ee05f38a291b81",
   "outputs": [],
   "execution_count": 1
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "To actually bind those schemas to a chat model, we'll use the .bind_tools() method. This handles converting the add and multiply schemas to the proper format for the model. The tool schema will then be passed it in each time the model is invoked.",
   "id": "cc815906bd93968a"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-01T06:24:39.899684Z",
     "start_time": "2025-08-01T06:24:27.986259Z"
    }
   },
   "cell_type": "code",
   "source": [
    "from init_llm_model import llm\n",
    "\n",
    "llm_with_tools = llm.bind_tools(tools)\n",
    "\n",
    "query = \"What is 3 * 12?\"\n",
    "\n",
    "ai_msg = llm_with_tools.invoke(query)\n",
    "print(ai_msg)"
   ],
   "id": "f2367d2b898e6e49",
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_0_5fe7debc-4360-4940-8038-a3c02f58b674', 'function': {'arguments': '{\"a\":3,\"b\":12}', 'name': 'multiply'}, 'type': 'function', 'index': 0}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 23, 'prompt_tokens': 246, 'total_tokens': 269, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 246}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_8802369eaa_prod0623_fp8_kvcache', 'id': '62a538c5-784b-44ba-ac7f-fb29d67d6e60', 'service_tier': None, 'finish_reason': 'tool_calls', 'logprobs': None}, id='run--bd0c09bf-0e9e-4736-a240-b092092e2450-0', tool_calls=[{'name': 'multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_0_5fe7debc-4360-4940-8038-a3c02f58b674', 'type': 'tool_call'}], usage_metadata={'input_tokens': 246, 'output_tokens': 23, 'total_tokens': 269, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 2
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "\n",
    "As we can see our LLM generated arguments to a tool! If tool calls are included in a LLM response, they are attached to the corresponding message or message chunk as a list of tool call objects in the .tool_calls attribute\n"
   ],
   "id": "90105aa7af341b47"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "from langchain_core.output_parsers import PydanticToolsParser\n",
    "from pydantic import BaseModel, Field\n",
    "\n",
    "\n",
    "class add(BaseModel):\n",
    "    \"\"\"Add two integers.\"\"\"\n",
    "\n",
    "    a: int = Field(..., description=\"First integer\")\n",
    "    b: int = Field(..., description=\"Second integer\")\n",
    "\n",
    "\n",
    "class multiply(BaseModel):\n",
    "    \"\"\"Multiply two integers.\"\"\"\n",
    "\n",
    "    a: int = Field(..., description=\"First integer\")\n",
    "    b: int = Field(..., description=\"Second integer\")\n",
    "\n",
    "\n",
    "chain = llm_with_tools | PydanticToolsParser(tools=[add, multiply])\n",
    "chain.invoke(query)"
   ],
   "id": "1f978a500e4fb059"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "query = \"What is 3 * 12? Also, what is 11 + 49?\"\n",
    "\n",
    "messages = [HumanMessage(query)]\n",
    "\n",
    "ai_msg = llm_with_tools.invoke(messages)\n",
    "\n",
    "print(ai_msg.tool_calls)\n",
    "\n",
    "messages.append(ai_msg)"
   ],
   "id": "34eb6e19436fb1e7"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "from langchain_core.tools import tool\n",
    "\n",
    "\n",
    "@tool\n",
    "def add(a: int, b: int) -> int:\n",
    "    \"\"\"Adds a and b.\"\"\"\n",
    "    return a + b\n",
    "\n",
    "\n",
    "@tool\n",
    "def multiply(a: int, b: int) -> int:\n",
    "    \"\"\"Multiplies a and b.\"\"\"\n",
    "    return a * b\n",
    "\n",
    "\n",
    "tools = [add, multiply]\n",
    "\n",
    "llm_with_tools = llm.bind_tools(tools)"
   ],
   "id": "201231f76eabf9e3"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "for tool_call in ai_msg.tool_calls:\n",
    "    selected_tool = {\"add\": add, \"multiply\": multiply}[tool_call[\"name\"].lower()]\n",
    "    tool_msg = selected_tool.invoke(tool_call)\n",
    "    messages.append(tool_msg)\n",
    "\n",
    "print(messages)"
   ],
   "id": "b994eedc18e3dc3d"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": "llm_with_tools.invoke(messages)",
   "id": "fe1d80aaa6578700"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "LangChain Runnables that accept string or dict input can be converted to tools using the as_tool method, which allows for the specification of names, descriptions, and additional schema information for arguments.",
   "id": "45adc42b259c0a16"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "from langchain_core.tools import StructuredTool\n",
    "\n",
    "from langchain_core.tools import ToolException\n",
    "\n",
    "\n",
    "def get_weather(city: str) -> int:\n",
    "    \"\"\"Get weather for the given city.\"\"\"\n",
    "    raise ToolException(f\"Error: There is no city by the name of {city}.\")\n",
    "\n",
    "get_weather_tool = StructuredTool.from_function(\n",
    "    func=get_weather,\n",
    "    handle_tool_error=True,\n",
    ")\n",
    "\n",
    "get_weather_tool.invoke({\"city\": \"foobar\"})"
   ],
   "id": "a760043977d92ed7"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "def _handle_error(error: ToolException) -> str:\n",
    "    return f\"The following errors occurred during tool execution: `{error.args[0]}`\"\n",
    "\n",
    "\n",
    "get_weather_tool = StructuredTool.from_function(\n",
    "    func=get_weather,\n",
    "    handle_tool_error=_handle_error,\n",
    ")\n",
    "\n",
    "get_weather_tool.invoke({\"city\": \"foobar\"})"
   ],
   "id": "a8fef001d74bf82e"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "The Tool and ToolMessage interfaces make it possible to distinguish between the parts of the tool output meant for the model (this is the ToolMessage.content) and those parts which are meant for use outside the model (ToolMessage.artifact).\n",
    "\n",
    "If we want our tool to distinguish between message content and other artifacts, we need to specify response_format=\"content_and_artifact\" when defining our tool and make sure that we return a tuple of (content, artifact):"
   ],
   "id": "869b1c28432a6d4"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "import random\n",
    "from typing import List, Tuple\n",
    "\n",
    "from langchain_core.tools import tool\n",
    "\n",
    "\n",
    "@tool(response_format=\"content_and_artifact\")\n",
    "def generate_random_ints(min: int, max: int, size: int) -> Tuple[str, List[int]]:\n",
    "    \"\"\"Generate size random ints in the range [min, max].\"\"\"\n",
    "    array = [random.randint(min, max) for _ in range(size)]\n",
    "    content = f\"Successfully generated array of {size} random ints in [{min}, {max}].\"\n",
    "    return content, array"
   ],
   "id": "8185f1122f0dd778"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "If we invoke our tool directly with the tool arguments, we'll get back just the content part of the output:\n",
    "If we invoke our tool with a ToolCall (like the ones generated by tool-calling models), we'll get back a ToolMessage that contains both the content and artifact generated by the Tool:"
   ],
   "id": "cded868650177b40"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": "generate_random_ints.invoke({\"min\": 0, \"max\": 9, \"size\": 10})",
   "id": "2e93be43b414479f"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "generate_random_ints.invoke(\n",
    "    {\n",
    "        \"name\": \"generate_random_ints\",\n",
    "        \"args\": {\"min\": 0, \"max\": 9, \"size\": 10},\n",
    "        \"id\": \"123\",  # required\n",
    "        \"type\": \"tool_call\",  # required\n",
    "    }\n",
    ")"
   ],
   "id": "3895667031bd038c"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "we can force our tool to call the multiply tool by using the following code:",
   "id": "5155fa7177cf67a1"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "llm_forced_to_multiply = llm.bind_tools(tools, tool_choice=\"multiply\")\n",
    "llm_forced_to_multiply.invoke(\"what is 2 + 4\")"
   ],
   "id": "38738d8fd86d8d60"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "We can also just force our tool to select at least one of our tools by passing in the \"any\" (or \"required\" which is OpenAI specific) keyword to the tool_choice parameter.",
   "id": "f9f94a0b4a2cc4d5"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "llm_forced_to_use_tool = llm.bind_tools(tools, tool_choice=\"any\")\n",
    "llm_forced_to_use_tool.invoke(\"What day is today?\")"
   ],
   "id": "eeb49302c4be6262"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "from langchain_core.messages import AIMessage, HumanMessage, ToolMessage\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_core.runnables import RunnablePassthrough\n",
    "\n",
    "examples = [\n",
    "    HumanMessage(\n",
    "        \"What's the product of 317253 and 128472 plus four\", name=\"example_user\"\n",
    "    ),\n",
    "    AIMessage(\n",
    "        \"\",\n",
    "        name=\"example_assistant\",\n",
    "        tool_calls=[\n",
    "            {\"name\": \"Multiply\", \"args\": {\"x\": 317253, \"y\": 128472}, \"id\": \"1\"}\n",
    "        ],\n",
    "    ),\n",
    "    ToolMessage(\"16505054784\", tool_call_id=\"1\"),\n",
    "    AIMessage(\n",
    "        \"\",\n",
    "        name=\"example_assistant\",\n",
    "        tool_calls=[{\"name\": \"Add\", \"args\": {\"x\": 16505054784, \"y\": 4}, \"id\": \"2\"}],\n",
    "    ),\n",
    "    ToolMessage(\"16505054788\", tool_call_id=\"2\"),\n",
    "    AIMessage(\n",
    "        \"The product of 317253 and 128472 plus four is 16505054788\",\n",
    "        name=\"example_assistant\",\n",
    "    ),\n",
    "]\n",
    "\n",
    "system = \"\"\"You are bad at math but are an expert at using a calculator. \n",
    "\n",
    "Use past tool usage as an example of how to correctly use the tools.\"\"\"\n",
    "few_shot_prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", system),\n",
    "        *examples,\n",
    "        (\"human\", \"{query}\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "chain = {\"query\": RunnablePassthrough()} | few_shot_prompt | llm_with_tools\n",
    "chain.invoke(\"Whats 119 times 8 minus 20\").tool_calls"
   ],
   "id": "12402ae94e405fe0"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
